#include "NetWork.h"
#include "timer.h"
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "MQTTClient.h"
#include "aliot_connect.h"
#include "userdata.h"
#include "aliot_dm.h"
#include "aliot_ota.h"
#include "iap_pro.h"
#include "core_sha256.h"



#ifndef USE_MODULE_MQTT
static bool s_bIsNetstart = false;
static bool s_bIsNetreport = false;
static bool s_bIsRequsetOta = false;
static NETWORK_STATUS   s_eNetworkStatus = NETWORK_IDLE;
static NETWORK_STATUS   s_eNetworkLastStatus = NETWORK_IDLE;

static Timer s_tNetworkTimer;

//产品密钥
static char* s_ucAliotProductKey = "a1W2Uba2nEW";
//设备名称
static char* s_ucAliotDeviceName = "TestDevice1";
//设备密钥
static char* s_ucAliotDeviceSecret = "c912e08b3aa69592656c971c1479cd2d";
//服务器地区，可在阿里云IOT控制台页面左上角和https://help.aliyun.com/document_detail/40654.html?spm=a2c4g.11186623.2.17.559573a8MfOuhn网站查看
static char* s_ucAliotRegin = "cn-shanghai";
//ALINK协议中的ID字段
static unsigned short s_pId = 1;

static Network      s_MqttNetwork;
static MQTTClient   s_MqttClient;


static uint8_t      s_ucMqttReceiveBuff[1460];
static int          s_iMqttReceiveLen = 0;
static int          s_iMqttReadLen = 0;

//MQTT协议栈buffer
static uint8_t      s_ucMqttSendBuffer[1460];
static uint8_t      s_ucMqttReceiveBuffer[1460];

#define UPDATE_FILE_NAME       "update.bin"

typedef struct
{
    char        cUrl[300];
    uint8_t     ucSha256[32];
    int32_t     ifilesize;
    bool        bUpdateFlag;
}UpdateParam;

typedef struct
{
    int     iTotalFilesize;
    int     iProgramSize;
    int     iFileHandle;
    bool    bProgramFlag;
}FileProgramParam;

static UpdateParam              s_UpdateParam;
static FileProgramParam         s_FileProgram;

void PrintHexArray(const uint8_t pkt[],int len)
{
	int i = 0;
	for(i = 0;i < len;i++)
	{
		printf("%02X ",pkt[i]);
	}
	printf("\r\n");
}

//TCP接收回调函数(module 4G模块层)
static void MqttTcpReceivePacket(int id,const uint8_t* pkt,int len)
{
    if(len > sizeof(s_ucMqttReceiveBuff))
        len = sizeof(s_ucMqttReceiveBuff);
    memcpy(s_ucMqttReceiveBuff,pkt,len);
    s_iMqttReceiveLen = len;
    s_iMqttReadLen = 0;
}

//TCP连接意外断开回调函数(module 4G模块层)
static void MqttTcpDisconnectCallback(int id)
{
	s_eNetworkLastStatus = NETWORK_CONNECTING;
    s_eNetworkStatus = NETWORK_WAIT;		//等待重连状态
    StartTimer(&s_tNetworkTimer,10*1000);
	s_MqttClient.isconnected = 0;		//强行把MQTT协议栈中的连接标志写0
}

//mqtt network 结构体中mqttread实现
int MqttTcpRead(struct Network* n, unsigned char* read_buffer, int num, int timeout)
{
    module_loop(&g_ATModule);
	if(s_iMqttReadLen < s_iMqttReceiveLen)
    {
        int len = s_iMqttReceiveLen - s_iMqttReadLen;
        if(len > num)
            len = num;
        memcpy(read_buffer,&s_ucMqttReceiveBuff[s_iMqttReadLen],len);
        s_iMqttReadLen += len;
        return len;
    }
    return 0;
}

//mqtt network结构体中mqttwrite实现
int MqttTcpWrite(struct Network* n, unsigned char* send_buffer, int num, int timeout)
{
    MODULE_ERRPO err = module_tcp_send(&g_ATModule,send_buffer,num);
    if(err == MODULE_ERR_NONE)
        return num;
    return 0;
}

//属性上报回应
void property_post_reply(MessageData* msg);
//事件上报回应
void event_post_reply(MessageData* msg);
//固件升级信息主题
void ota_info_pub(MessageData* msg);

//属性上报
void property_publish(enum QoS q);
//版本号上报
void version_publish(void);
//OTA进度上报
void ota_process_publish(int step);
//主动拉取OTA升级信息
void ota_request_publish(void);

static void NetworkFailHandle(void)
{
    s_eNetworkLastStatus = s_eNetworkStatus;
    s_eNetworkStatus = NETWORK_WAIT;
    StartTimer(&s_tNetworkTimer,10*1000);
}

void NetworkLoop(void)
{
    switch(s_eNetworkStatus)
    {
        case NETWORK_IDLE:      //空闲状态
            if(!s_bIsNetstart)
                return;
            if(MODULE_ERR_NONE != module_start(&g_ATModule))    //执行初始化指令
            {
                NetworkFailHandle();
                return;
            }
        case NETWORK_CONNECTING:        //连接过程
		{
			char ucMqttUsername[128];
			char ucMqttPassword[128];
			char ucMqttClientid[128];
			char ucMqttUrl[128];
            char* topic1;
            char* topic2;
            MQTTPacket_connectData mqttConnectOptions = MQTTPacket_connectData_initializer;
			if(NULL == module_get_ip(&g_ATModule))
            {
                NetworkFailHandle();
                return;
            }
            //生成用户名
			auth_mqtt_username(ucMqttUsername, s_ucAliotProductKey, s_ucAliotDeviceName);
            //生成密码
			auth_mqtt_password(ucMqttPassword, s_ucAliotProductKey, s_ucAliotDeviceName,s_ucAliotDeviceSecret);
			//client id
            auth_mqtt_clientid(ucMqttClientid, s_ucAliotProductKey, s_ucAliotDeviceName, "3");
			//生成URL
            auth_mqtt_url(ucMqttUrl,s_ucAliotProductKey,s_ucAliotRegin);
            
            mqttConnectOptions.username.cstring = ucMqttUsername;
            mqttConnectOptions.password.cstring = ucMqttPassword;
            mqttConnectOptions.clientID.cstring = ucMqttClientid;
            //mqttConnectOptions.keepAliveInterval = 100;
            
            //连接MQTT服务器
            if(MODULE_ERR_NONE != module_tcp_connect(&g_ATModule,ucMqttUrl,1883))
            {
                NetworkFailHandle();
				module_tcp_disconnect(&g_ATModule);
                return;
            }
            //登录MQTT服务器
            if(SUCCESS != MQTTConnect(&s_MqttClient, &mqttConnectOptions))
            {
                NetworkFailHandle();
				module_tcp_disconnect(&g_ATModule);
                return;
            }
            s_eNetworkStatus = NETWORK_CONNECTED;
            //订阅属性回复主题
            dm_property_post_reply(&topic1,s_ucAliotProductKey, s_ucAliotDeviceName);
            MQTTSubscribe(&s_MqttClient, topic1, QOS1, property_post_reply);
            
            //订阅固件升级信息主题
            ota_info_sub(&topic2,s_ucAliotProductKey, s_ucAliotDeviceName);
            MQTTSubscribe(&s_MqttClient, topic2, QOS1, ota_info_pub);
            
            //在网络连接成功并且订阅完各主题后，上报一次版本号
            version_publish();
		}
        case NETWORK_CONNECTED:
            if(!s_bIsNetstart)  //断开网络标志
            {
                MQTTDisconnect(&s_MqttClient);
                module_tcp_disconnect(&g_ATModule);
                s_eNetworkStatus = NETWORK_IDLE;
			}
            if(s_bIsNetreport)  //属性上报标志
            {
                property_publish(QOS0);
                s_bIsNetreport = false;
            }
            if(s_UpdateParam.bUpdateFlag)   //升级标志
            {
                s_UpdateParam.bUpdateFlag = false;
                if(module_https_config(&g_ATModule) != MODULE_ERR_NONE)
                {
                    printf("http config fail!\r\n");
					ota_process_publish(OTA_DOWNLOAD_FAIL);
                    return;
                }
                if(module_https_connect(&g_ATModule,s_UpdateParam.cUrl) != MODULE_ERR_NONE)
                {
                    printf("http connect fail!\r\n");
					ota_process_publish(OTA_DOWNLOAD_FAIL);
                    return;
                }
                if(module_https_get_to_file(&g_ATModule,UPDATE_FILE_NAME,60,&s_UpdateParam.ifilesize) != MODULE_ERR_NONE)
                {
                    printf("http download fail!\r\n");
					ota_process_publish(OTA_DOWNLOAD_FAIL);
                    return;
                }
				s_FileProgram.iTotalFilesize = s_UpdateParam.ifilesize;
                //到这里下载成功，开始编程FLASH
                if(module_file_open(&g_ATModule,UPDATE_FILE_NAME,&s_FileProgram.iFileHandle) == MODULE_ERR_NONE)
                {
                    s_FileProgram.bProgramFlag = true;
                    FlashProgramStart();
                    EraseApp2();
                    ota_process_publish(50);       //下载完成就上报进度50
                }
                
            }
            if(s_FileProgram.bProgramFlag)  //烧写标志
            {
                //烧写
                static uint8_t s_fileBuff[1024];
                if(s_FileProgram.iProgramSize < s_FileProgram.iTotalFilesize)
                {
                    int readLen = 0;        //要求读取的字节
                    int realLen = 0;        //真实读取到的字节
                    if(s_FileProgram.iTotalFilesize - s_FileProgram.iProgramSize > 1024)
                        readLen = 1024;
                    else
                        readLen = s_FileProgram.iTotalFilesize - s_FileProgram.iProgramSize;
                    if(MODULE_ERR_NONE == module_file_read(&g_ATModule,s_FileProgram.iFileHandle,s_FileProgram.iProgramSize,readLen,&realLen,s_fileBuff))
                    {
                        ProgramFlash(FLASH_APP2_ADDRESS + s_FileProgram.iProgramSize,s_fileBuff,realLen);
                        s_FileProgram.iProgramSize += realLen;
						printf("program:%d/%d\r\n",s_FileProgram.iProgramSize,s_FileProgram.iTotalFilesize);
                    }
                }
                else
                {
                    //校验+重启
                    uint8_t sha256[32];
                    module_file_close(&g_ATModule,s_FileProgram.iFileHandle);   //全部读完数据，关闭文件
                    core_sha256((const uint8_t*)FLASH_APP2_ADDRESS,s_FileProgram.iTotalFilesize,sha256);
                    if(memcmp(sha256,s_UpdateParam.ucSha256,32) == 0)
                    {
                        //校验通过，写入标志位后重启
                        UpdateRunFlag updateflg;
                        GetRunFlag(&updateflg);
                        updateflg.UpdateHandle = UPDATE_FLG;
                        updateflg.App2Size = s_FileProgram.iTotalFilesize;
                        memcpy(updateflg.App2SHA256,sha256,32);
                        SetRunFlag(&updateflg);
                        ota_process_publish(99);                //烧写完成就上报进度99
                        MQTTDisconnect(&s_MqttClient);          //退出登录
                        module_tcp_disconnect(&g_ATModule);     //断开连接
						printf("program success! reboot!\r\n");
                        Reboot();                               //重启
                    }
                    else
					{
                        ota_process_publish(OTA_PROGRAM_FAIL);
						printf("program fail! \r\n");
					}
                    s_FileProgram.bProgramFlag = false;
                }
            }
			if(s_bIsRequsetOta)
			{
				s_bIsRequsetOta =false;
				ota_request_publish();
			}
            break;
        case NETWORK_WAIT:
            if(TimeOut(&s_tNetworkTimer))
            {
                s_eNetworkStatus = s_eNetworkLastStatus;
            }
            break;
            default:break;
    }
	module_loop(&g_ATModule);
    MQTTYield(&s_MqttClient,100);
}

//网络初始化
void NetworkInit(void)
{
	ATModuleInit(GetTimeTick);
    module_set_tcp_receive_callback(&g_ATModule,MqttTcpReceivePacket);
	s_MqttNetwork.mqttread = MqttTcpRead;
    s_MqttNetwork.mqttwrite = MqttTcpWrite;
    MQTTClientInit(&s_MqttClient,&s_MqttNetwork,35*1000,s_ucMqttSendBuffer,sizeof(s_ucMqttSendBuffer),
        s_ucMqttReceiveBuffer,sizeof(s_ucMqttReceiveBuffer));
    
    s_UpdateParam.bUpdateFlag = false;
    s_FileProgram.bProgramFlag = false;
}

//属性上报回应
void property_post_reply(MessageData* msg)
{
    //因为不关心返回值，因此收到属性上报ACK后不做任何处理
}

//事件上报回应
void event_post_reply(MessageData* msg)
{
    //因为不关心返回值，因此收到事件上报ACK后不做任何处理
}

//固件升级信息主题处理函数
void ota_info_pub(MessageData* msg)
{
    /* payload格式
    {
        "code": "1000",
        "data": {
        "size": 432945,
        "version": "2.0.0",
        "isDiff": 1,
        "url": "https://iotx-ota-pre.oss-cn-shanghai.aliyuncs.com",
        "md5": "93230c3bde425a9d7984a594ac55ea1e",
        "sign": "93230c3bde425a9d7984a594ac55****",
        "signMethod": "Md5",
        "module": "MCU",
        "extData":{
            "key1":"value1",
            "key2":"value2"
         }
      },
      "id": "1507707025",
      "message": "success"
    */
    
   cJSON            *rootJson;
   cJSON            *dataJson;
   rootJson = cJSON_Parse(msg->message->payload);
   if(rootJson)
   {
       cJSON    *versionJson;
       cJSON    *urlJson;
       cJSON    *methodJson;
       cJSON    *signJson;
       cJSON    *fileSizeJson;
       dataJson         = cJSON_GetObjectItem(rootJson,"data");
	   printf("ota json:%s\r\n",msg->message->payload);
       if(dataJson)
       {
           versionJson      = cJSON_GetObjectItem(dataJson,"version");
           urlJson          = cJSON_GetObjectItem(dataJson,"url");
           methodJson       = cJSON_GetObjectItem(dataJson,"signMethod");
           signJson         = cJSON_GetObjectItem(dataJson,"sign");
           fileSizeJson     = cJSON_GetObjectItem(dataJson,"size");
           if(!versionJson || !urlJson || !methodJson||!signJson||!fileSizeJson)
               goto safe_free;

           //比较版本号
           if(strcmp(versionJson->valuestring,ucSoftwareVersion) == 0)
               goto safe_free;

           //SHA256验证
           if(strcmp(methodJson->valuestring,"SHA256") != 0 && strcmp(methodJson->valuestring,"Sha256") != 0)
               goto safe_free;

           //SHA256签名值
           str_to_hex(signJson->valuestring,s_UpdateParam.ucSha256,32);
           
           //记录URL
           strncpy(s_UpdateParam.cUrl,urlJson->valuestring,300);
           
           //文件大小
           s_UpdateParam.ifilesize = fileSizeJson->valueint;
           
           //升级标志
           s_UpdateParam.bUpdateFlag = true;
       }
       safe_free:
       cJSON_Delete(rootJson);
   }
}

//属性上报
void property_publish(enum QoS q)
{
    char* topic;
    char* payload;
    cJSON* paramObj;
    MQTTMessage msg;
    //新增一个参数组JSON对象
    paramObj = cJSON_CreateObject();
    /******增加自定义的物模型字段和值******/
    //增加温度
    cJSON_AddNumberToObject(paramObj, "Temp", g_sAppData.Temp);
    //增加湿度
    cJSON_AddNumberToObject(paramObj, "Humidity", g_sAppData.Humidity);
    //生成json字符串
    msg.payloadlen = dm_property_json(&payload,s_pId,paramObj);
    //msg赋值
    msg.payload = payload;
    msg.qos = q;
    msg.dup = 0;
    msg.id = s_pId++;
    //生成属性上报主题
    dm_property_post(&topic,s_ucAliotProductKey, s_ucAliotDeviceName);
    //属性推送
    MQTTPublish(&s_MqttClient,topic , &msg);
    
    //释放内存
    free(topic);
    free(payload);
    //由于在dm_property_json已经释放了json的内存因此不需要调用cJSON_Delete(paramObj)再次释放;
}

//版本号上报
void version_publish(void)
{
    char* topic;
    char* payload;
    MQTTMessage msg;
    //生成json字符串
    msg.payloadlen = version_json(&payload,s_pId,ucSoftwareVersion);
    //msg赋值
    msg.payload = payload;
    msg.qos = QOS1;
    msg.dup = 0;
    msg.id = s_pId++;
    //生成版本上报主题
    version_post(&topic,s_ucAliotProductKey, s_ucAliotDeviceName);
    //属性推送
    MQTTPublish(&s_MqttClient,topic , &msg);
    
    //释放内存
    free(topic);
    free(payload);
}

//OTA进度上报
void ota_process_publish(int step)
{
    char* topic;
    char* payload;
    MQTTMessage msg;
    //生成json字符串
    msg.payloadlen = ota_process_json(&payload,s_pId,step);
    //msg赋值
    msg.payload = payload;
    msg.qos = QOS1;
    msg.dup = 0;
    msg.id = s_pId++;
    //生成版本上报主题
    ota_process_post(&topic,s_ucAliotProductKey, s_ucAliotDeviceName);
    //属性推送
    MQTTPublish(&s_MqttClient,topic , &msg);
    
    //释放内存
    free(topic);
    free(payload);
}

//主动拉取OTA升级信息
void ota_request_publish(void)
{
	char* topic;
    char* payload;
    MQTTMessage msg;
    //生成json字符串
    msg.payloadlen = ota_request_payload(&payload,s_pId);
    //msg赋值
    msg.payload = payload;
    msg.qos = QOS1;
    msg.dup = 0;
    msg.id = s_pId++;
    //生成版本上报主题
    ota_request_topic(&topic,s_ucAliotProductKey, s_ucAliotDeviceName);
    //属性推送
    MQTTPublish(&s_MqttClient,topic , &msg);
    
    //释放内存
    free(topic);
    free(payload);
}

//启动网络
void NetworkStart(void)
{
	if(!s_bIsNetstart)
	{
		module_poweron(&g_ATModule);
		s_bIsNetstart = true;
	}
}


//上报一次属性
void NetworkReport(void)
{
    s_bIsNetreport = true;
}

//停止网络
void NetworkStop(void)
{
    
    s_bIsNetstart = false;
}

//拉取升级信息
void NetworkRequsetOTA(void)
{
	s_bIsRequsetOta = true;
}

//获取当前网络状态
NETWORK_STATUS GetNetworkStatus(void)
{
    return s_eNetworkStatus;
}
#endif
